package com.yunchao.device

import com.rscja.deviceapi.PSAM
import com.rscja.deviceapi.RFIDWithUHFUART
import com.rscja.deviceapi.interfaces.IUHF
import com.yunchao.lock.DeviceHelper
import com.yunchao.lock.IDeviceHelper.Companion.LOOP_SCAN_POWER
import com.yunchao.lock.IDeviceHelper.Companion.PSAM_DEFAULT_HEX_DATA
import com.yunchao.lock.IDeviceHelper.Companion.PSAM_SUCCEED_INFO
import com.yunchao.lock.IDeviceHelper.Companion.RFID_POWER_ERROR_MESSAGE
import com.yunchao.lock.IDeviceHelper.Companion.RFID_READ_LOCK_INFO_MESSAGE
import com.yunchao.lock.IDeviceHelper.Companion.RFID_START_LOOP_SCAN_MESSAGE
import com.yunchao.lock.IDeviceHelper.Companion.RFID_STOP_LOOP_SCAN_MESSAGE
import com.yunchao.lock.IDeviceHelper.Companion.SINGLE_SCAN_POWER
import com.yunchao.lock.LockAction
import com.yunchao.lock.LockEPCInfo
import com.yunchao.lock.checkLock
import com.yunchao.lock.getLockEPCInfo
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.currentCoroutineContext
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.isActive
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext
import timber.log.Timber

class DeviceHelperImpl() : DeviceHelper() {

    private val mDevice: RFIDWithUHFUART by lazy { RFIDWithUHFUART.getInstance() }

    private val mPsam: PSAM by lazy { PSAM.getInstance() }

    override suspend fun openDevice(): Boolean = withContext(Dispatchers.Default) {
        runCatching {
            val deviceOpenResult = async { mDevice.init() }.await()
            val deviceHidePC =
                async { mDevice.setPower(SINGLE_SCAN_POWER) }.await()
            val psamOpenResult = async { mPsam.init() }.await()

            deviceOpenResult && deviceHidePC && psamOpenResult
        }.getOrDefault(false)
    }

    override suspend fun closeDevice(): Boolean = withContext(Dispatchers.Default) {
        runCatching {
            mDevice.free() && mPsam.free()
        }.getOrDefault(false)
    }


    override suspend fun inventoryOnce(): LockEPCInfo = withContext(Dispatchers.Default) {
        runCatching {
            mDevice.run {
                val tagInfo = mDevice.inventorySingleTag()
                require(tagInfo != null && tagInfo.epc != null) { RFID_READ_LOCK_INFO_MESSAGE }
                runCatching { getLockEPCInfo(tagInfo.epc) }.getOrElse { throw it }
            }
        }.getOrElse { throw it }
    }

    override fun inventoryLoop(): Flow<List<LockEPCInfo>> = flow {
        require(mDevice.setPower(LOOP_SCAN_POWER)) { RFID_POWER_ERROR_MESSAGE }
        require(mDevice.startInventoryTag()) { RFID_START_LOOP_SCAN_MESSAGE }
        try {
            while (currentCoroutineContext().isActive) {
                runCatching {
                    val epcInfo = getLockEPCInfo(mDevice.readTagFromBuffer().epc)
                    emit(listOf(epcInfo))
                }.getOrElse { emit(emptyList()) }
            }
        } finally {
            require(mDevice.stopInventory()) { RFID_STOP_LOOP_SCAN_MESSAGE }
        }
    }

    override suspend fun inventoryStop(): Boolean = withContext(Dispatchers.Default) {
        runCatching {
            mDevice.setPower(SINGLE_SCAN_POWER)
        }.getOrDefault(false)
    }

    override suspend fun setPower(isLoop: Boolean): Boolean = withContext(Dispatchers.Default) {
        runCatching {
            mDevice.setPower(if (isLoop) LOOP_SCAN_POWER else SINGLE_SCAN_POWER)
        }.getOrDefault(false)
    }


    override suspend fun readTagData(
        epcId: String,
        password: String,
        bank: Int,
        offset: Int,
        len: Int
    ): String = withContext(Dispatchers.Default) {
        mDevice.readData(
            password,
            IUHF.Bank_EPC, 32, 32, epcId.substring(0, 8), bank, offset, len
        )
    }

    override suspend fun writeTagData(
        epcId: String,
        password: String,
        bank: Int,
        offset: Int,
        len: Int,
        data: String
    ): Boolean = withContext(Dispatchers.Default) {
        mDevice.writeData(
            password, IUHF.Bank_EPC, 32, 32, epcId.substring(0, 8), bank, offset, len, data
        )
    }

    override suspend fun readLockStatus(
        lockEPCInfo: LockEPCInfo,
        password: String,
        action: LockAction
    ): LockEPCInfo = withContext(Dispatchers.Default) {
        val result = readTagData(lockEPCInfo.epc, password, IUHF.Bank_EPC, 2, 6)

        require(result.isNotEmpty()) { RFID_READ_LOCK_INFO_MESSAGE }

        getLockEPCInfo(result).checkLock(action)
    }

    override suspend fun openPSAM(): Boolean = mPsam.init()

    override suspend fun authPSAM(): Boolean = mPsam.executeCmd("02", "01")?.let {
        val cmdVoltage = "6E"
        val param = "000" + "9600"
        sendApdu(cmdVoltage, param)
    }?.let {
        sendApdu("00", PSAM_DEFAULT_HEX_DATA)
    }?.let {
        PSAM_SUCCEED_INFO == it
    } ?: false

    override suspend fun sendApdu(cmd: String, data: String): String = psamMutex.withLock {
        Timber.d("PSAM卡 发送的信息：$data")
        val result = mPsam.executeCmd(cmd, data)
        Timber.d("PSAM卡 接收的信息：$result")
        result
    }
}